package edu.asu.sapa.ground;


import edu.asu.sapa.ground.update.Context;
import edu.asu.sapa.ground.update.Update;
import edu.asu.sapa.ground.update.UpdateWrapper;
import edu.asu.sapa.utils.PriorityQueue;

public class UpdateQ<T extends Update> extends PriorityQueue<Update, UpdateWrapper<T>> {

	public UpdateQ() {
		table = new UpdateWrapper[DEFAULT];
	}

	public UpdateQ(int capacity) {
		if (capacity < DEFAULT)
			capacity = DEFAULT;
		table = new UpdateWrapper[capacity];
	}

	public UpdateQ(UpdateQ<T> queue) {
		int s = queue.size;
		UpdateWrapper<T>[] eSrc = queue.table;
		UpdateWrapper<T>[] eDst = new UpdateWrapper[s+RESERVE]; 
		table = eDst;
		size = s;
//		for (int i = 0; i < s; ++i) {
//			eDst[i] = eSrc[i].clone();
//		}
		System.arraycopy(eSrc,0,eDst,0,s);
	}
	
	public UpdateQ<T> clone() {
		UpdateQ<T> ret=null;
		try {
			ret = (UpdateQ<T>) super.clone();
		} catch(Exception e) {
			// assert false;
		}
		
		int s = size;
		UpdateWrapper<T>[] eSrc = table;
		UpdateWrapper<T>[] eDst = new UpdateWrapper[s+RESERVE]; 
		ret.table = eDst;
		ret.size = s;
//		for (int i = 0; i < s; ++i) {
//			eDst[i] = eSrc[i].clone();
//		}
		System.arraycopy(eSrc,0,eDst,0,s);
		return ret;
	}

	
	public final UpdateWrapper<T> newElement() {
		return new UpdateWrapper<T>();
	}

	public final boolean add(UpdateWrapper<T> w) {
		UpdateWrapper<T>[] evts = table;

		int i = size;
		if (i >= evts.length) {
			evts = growArray(evts);
		}
		++size;
		
		float time = w.time;
		int type = w.type;
		int parent;
		UpdateWrapper<T> p;
		for ( ; (parent = (i - 1) >> 1) >= 0 && (time < (p=evts[parent]).time || type < p.type); i = parent) {
			evts[i] = p;
		}
		evts[i] = w;
		
		return true;
	}	
	
	
	public final boolean add(Context c, Update event, int type, float time) {
		UpdateWrapper<T>[] evts = table;
		
		int i = size;
		if (i >= evts.length) {
			evts = growArray(evts);
			
		}
		UpdateWrapper<T> t;//=evts[i];
//		if (t == null) {
			t = new UpdateWrapper<T>(c,event,type,time);
//		} else {
//			t.set(c,event,type,time);
//		}
		++size;
		
		int parent;
		UpdateWrapper<T> p;
		for (; (parent = (i - 1) >> 1) >= 0 && (time < (p=evts[parent]).time || type < p.type); i = parent) {
			evts[i] = p;
		}
		evts[i] = t;
		
		return true;
	}
	
	public UpdateWrapper poll(float time, int type) {
		int s=size;
		if (s <= 0)
			return null;
		// recycle memory aggressively
		UpdateWrapper<T>[] evts = table;
		UpdateWrapper<T> down_event = evts[--s];
		UpdateWrapper<T> ret = evts[0];
		if (ret.time > time && ret.type > type)
			return null;
		size = s;
		evts[s] = ret;
		float t = down_event.time;
		int ty = down_event.type;
		int i = 0;
		float u;
		int x;
		int child;
		UpdateWrapper<T> a,b;
		// events[i] = down_event; // should be unnecessary
		for (; (child = (i << 1) + 1) < s; i = child) {
			a=evts[child];
			u=a.time;
			x=a.type;
			if (child + 1 < s
					&& (((b=evts[child + 1]).time) < u || b.type < x)) {
				++child;
				a=b;
				u=b.time;
				x=b.type;
			}
			if (u < t || x < ty) {
				evts[i] = a;
			} else {
				break;
			}
		}
		evts[i] = down_event;
		return ret;
	}
	
	UpdateWrapper<T> removeInternal(int s, UpdateWrapper<T>[] evts) {
		UpdateWrapper<T> down_event = evts[s];
		UpdateWrapper<T> ret = evts[0];
		evts[s] = ret;
		float t = down_event.time;
		int ty = down_event.type;
		int i = 0;
		float u;
		int x;
		int child;
		UpdateWrapper<T> a,b;
		// events[i] = down_event; // should be unnecessary
		for (; (child = (i << 1) + 1) < s; i = child) {
			a=evts[child];
			u=a.time;
			x=a.type;
			if (child + 1 < s
					&& (((b=evts[child + 1]).time) < u || b.type < x)) {
				++child;
				a=b;
				u=b.time;
				x=b.type;
			}
			if (u < t || x < ty) {
				evts[i] = a;
			} else {
				break;
			}
		}
		evts[i] = down_event;
		return ret;
	}
	
	public boolean processBefore(State s,float time) {
		int sz=size;
		UpdateWrapper<T>[] evts=table;
		UpdateWrapper<T> top;
		while (sz > 0) {
			top = evts[0];
			if (top.time > time) {
				size = sz;
				return true;
			}
			if (top.type >= UpdateWrapper.Before_After && top.time == time) {
				size=sz;
				return true;
			}
			this.removeInternal(--sz,evts);
			if (!top.process(s)) {
				size = sz;
				return false;
			}
		}
		size = sz;
		return true;
	}
	
	public final void growArray() {
		UpdateWrapper<T>[] dst = new UpdateWrapper[size+GROW_BY];
		System.arraycopy(table,0,dst,0,size);
		table = dst;
	}
	
	public final UpdateWrapper<T>[] growArray(UpdateWrapper<T>[] evts) {
		UpdateWrapper<T>[] dst = new UpdateWrapper[size+GROW_BY];
		System.arraycopy(evts,0,dst,0,size);
		table = dst;
		return dst;
	}
	
	public UpdateWrapper<T>[] toArray() {
		int sz = size;
		UpdateWrapper<T>[] ret = new UpdateWrapper[sz];
		for (int i=0; i < size; ++i)
			ret[i] = table[i].clone();
		return ret;
	}
	
	
}
